home *** CD-ROM | disk | FTP | other *** search
Wrap
/* Miranda Installer - Installs nightlies and Miranda addons. Copyright (C) 2002-2003 Goblineye Entertainment Authors: Saar (Tornado) and Kai (kai_b) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "PackageSelector.h" #pragma hdrstop // fills with the current RECT of the package static void PS_GetPackageRect(HWND hWnd,const PS_CONTROLINFO *pCtrlInfo,int iPosY,const PS_PACKAGELIST *pPackage,RECT *pRect) { RECT rcWnd; GetClientRect(hWnd,&rcWnd); pRect->left = 1; pRect->top = iPosY + (pPackage->TitleSize.cy >> 1) - ((pCtrlInfo->wRowHeight-(PS_PACKAGESPACE>>1)) >> 1); pRect->right = rcWnd.right - 1; pRect->bottom = pRect->top + pCtrlInfo->wRowHeight + ((pPackage->dwFlags & 1) ? (pCtrlInfo->wFileHeight*pPackage->wTotalFiles) : (0)); } void PS_DrawControl(HWND hWnd,HDC hDC,RECT *pUpdateRect) { // pUpdateRect must not be NULL // it points to the invalidation RECT // should be whole client area of control if you want to draw all of it PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); HRGN hClippingRgn = CreateRectRgnIndirect(pUpdateRect); // there's no flickering when drawing even when using a small invalidation rect // because windows clips everything to the paint rect!! GDI is nice :) // drawing is done package-by-package // the painting RECT is checked against the RECT of the package // it is, of course, to avoid flickering and un-necessary drawing HFONT hOldFont = (HFONT)SelectObject(hDC,pCtrlInfo->hTitleFont); // select our favourite font // BOOL bIsEnabled = IsWindowEnabled(hWnd); // used for colors (hate BOOL, but conversion could be dangerous) RECT rcWnd; GetClientRect(hWnd,&rcWnd); // the background region is created from the update RECT, that way // less drawing will be needed // if at all :) // this is drawn right at the end // it only fills the missing "holes" // for now, the "hole" can only be at the bottom of the control // the background is erased for each and every package individually later on HRGN hBKRgn = CreateRectRgnIndirect(pUpdateRect); // region will be used to fill background when we're done // hmm // we have to remove the border from the background region // but instead of removing it (too much code), we'll AND hBKRgn with the drawing area of the control InflateRect(&rcWnd,-1,-1); // remove border (just for a sec) HRGN hTempRgn = CreateRectRgnIndirect(&rcWnd); InflateRect(&rcWnd,1,1); // back CombineRgn(hBKRgn,hBKRgn,hTempRgn,RGN_AND); // only touch drawing area (btw - params are fine according to docs) DeleteObject(hTempRgn); PS_PACKAGELIST *pCurPackage = pCtrlInfo->pPackageList; int iPosY = PS_PACKAGEPOS_Y - pCtrlInfo->wScrollPos; SetBkMode(hDC,TRANSPARENT); while (pCurPackage != NULL) { RECT rcItem; PS_GetPackageRect(hWnd,pCtrlInfo,iPosY,pCurPackage,&rcItem); // check if the package rect (that include files) is in the update region if (!RectInRegion(hClippingRgn,&rcItem)) { // nope, continue iPosY += pCtrlInfo->wRowHeight + ((pCurPackage->dwFlags & 1) ? (pCtrlInfo->wFileHeight*pCurPackage->wTotalFiles) : (0)); pCurPackage = pCurPackage->next; continue; } // draw background // if selected - selection background // if not - just the "white" background FillRect(hDC,&rcItem,((pCurPackage->packageInfo.fSelected) ? (pCtrlInfo->hBKBrush) : (GetSysColorBrush(COLOR_WINDOW)))); // take off HRGN hItemRgn = CreateRectRgnIndirect(&rcItem); // XOR will work because hItemRgn is ALWAYS part of hBKRgn // otherwise we wouldn't be here (if (!RectInRegion() would have kicked us to the next package) CombineRgn(hBKRgn,hItemRgn,hBKRgn,RGN_XOR); // take out hItemRgn (according to docs, this is OK) DeleteObject(hItemRgn); SetTextColor(hDC,/*((bIsEnabled) ? (*/GetSysColor(COLOR_WINDOWTEXT)/*) : (RGB(120,120,120)))*/); // window text // check if hot tracked if (pCurPackage->dwFlags & 2) { HPEN hVOPen = (HPEN)SelectObject(hDC,pCtrlInfo->hTitleUL); MoveToEx(hDC,PS_PACKAGEPOS_X,iPosY + pCurPackage->TitleSize.cy,NULL); LineTo(hDC,PS_PACKAGEPOS_X + pCurPackage->TitleSize.cx,iPosY + pCurPackage->TitleSize.cy); SelectObject(hDC,hVOPen); } // the actual text TextOut(hDC,PS_PACKAGEPOS_X,iPosY,pCurPackage->lpDisplayText,lstrlen(pCurPackage->lpDisplayText)); // remember the old pen HPEN hOldPen = (HPEN)SelectObject(hDC,pCtrlInfo->hTitleLine); // package line MoveToEx(hDC,PS_PACKAGEPOS_X + pCurPackage->TitleSize.cx + 5,iPosY + (pCurPackage->TitleSize.cy >> 1),NULL); LineTo(hDC,rcWnd.right - 5,iPosY + (pCurPackage->TitleSize.cy >> 1)); SetTextColor(hDC,RGB(90,90,90)); // I like it... bah :) char szPackageDesc[128], szPackageSize[32]; // format size first Format_DisplaySize(szPackageSize,pCurPackage->dwTotalSize); wsprintf(szPackageDesc,Translate("%d file/s (%s)"),pCurPackage->wTotalFiles,szPackageSize); SIZE tempSize; // used temp for drawing of description and possibly PACKAGESELECTOR_OPTIONALTEXT GetTextExtentPoint32(hDC,szPackageDesc,lstrlen(szPackageDesc),&tempSize); HFONT hPrevFont = (HFONT)SelectObject(hDC,pCtrlInfo->hPackageInfoFont); if (rcWnd.right-tempSize.cx-5 > (rcWnd.right * 0.4)) // small enough to be drawn { // draw description TextOut(hDC,rcWnd.right-tempSize.cx-5,iPosY+(pCurPackage->TitleSize.cy >> 1)+1,szPackageDesc,lstrlen(szPackageDesc)); } // now draw optional (maybe) if (pCurPackage->packageInfo.fOptional) { GetTextExtentPoint32(hDC,PS_OPTIONALTEXT,lstrlen(PS_OPTIONALTEXT),&tempSize); if (rcWnd.right-tempSize.cx-5 > (rcWnd.right * 0.4)) // small enough to be drawn { TextOut(hDC,rcWnd.right-tempSize.cx-5,iPosY-tempSize.cy+(pCurPackage->TitleSize.cy >> 1),PS_OPTIONALTEXT,lstrlen(PS_OPTIONALTEXT)); } } SelectObject(hDC,hPrevFont); SelectObject(hDC,hOldPen); // ok now let's draw icon for package and such hOldPen = (HPEN)SelectObject(hDC,pCtrlInfo->hCBPen); HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC,(HBRUSH)GetStockObject(NULL_BRUSH)); RECT rcIcon; rcIcon.top = iPosY + 1; rcIcon.bottom = rcIcon.top + pCurPackage->TitleSize.cy - 1; rcIcon.left = (PS_PACKAGEPOS_X - (rcIcon.bottom-rcIcon.top)) >> 1; rcIcon.right = (rcIcon.bottom - rcIcon.top) + rcIcon.left; RoundRect(hDC,rcIcon.left,rcIcon.top,rcIcon.right,rcIcon.bottom,2,2); if (pCurPackage->packageInfo.fSelected) { InflateRect(&rcIcon,-1,-1); // make it smaller Ellipse(hDC,rcIcon.left+1,rcIcon.top+1,rcIcon.right-1,rcIcon.bottom-1); } SelectObject(hDC,hOldPen); SelectObject(hDC,hOldBrush); // done icon // clean up... if (pCurPackage->dwFlags & 1) // expanded { // before we start, draw the entire line we're going to need for the files // usign rcIcon for this int iMidIcon = (int)(((rcIcon.right-rcIcon.left)>>1) + rcIcon.left); // remember there's at least one file in each package! // this is the line from the icon to the middle of the last file InflateRect(&rcIcon,1,1); // inflate it again hOldPen = (HPEN)SelectObject(hDC,pCtrlInfo->hTitleLine); MoveToEx(hDC,iMidIcon,rcIcon.bottom,NULL); LineTo(hDC,iMidIcon,iPosY+(pCtrlInfo->wFileHeight * pCurPackage->wTotalFiles)+(pCtrlInfo->wFileHeight>>1)); // ok now for the actual files PS_FILELIST *pCurFile = pCurPackage->pFileList; int iFilePos = iPosY + (int)((float)pCtrlInfo->wFileHeight*1.5f); // position at which we draw (first fileheight, then another half to get at position) while (pCurFile != NULL) { iPosY += pCtrlInfo->wFileHeight; // the actual size of the font // draw the filename TextOut(hDC,PS_FILEPOS_X,iPosY,pCurFile->lpDisplayText,lstrlen(pCurFile->lpDisplayText)); // now let's also do the other thingy, with the pen... :) MoveToEx(hDC,iMidIcon,iFilePos,NULL); LineTo(hDC,PS_FILEPOS_X,iFilePos); iFilePos += pCtrlInfo->wFileHeight; pCurFile = pCurFile->next; } SelectObject(hDC,hOldPen); } // row height includes space iPosY += pCtrlInfo->wRowHeight; pCurPackage = pCurPackage->next; } // fill the rest of the background rgn FillRgn(hDC,hBKRgn,/*((bIsEnabled) ? (*/GetSysColorBrush(COLOR_WINDOW)/*) : ((HBRUSH)GetStockObject(LTGRAY_BRUSH)))*/); // delete clipping region DeleteObject(hClippingRgn); DeleteObject(hBKRgn); // should be at end FrameRect(hDC,&rcWnd,(HBRUSH)GetStockObject(GRAY_BRUSH)); SelectObject(hDC,hOldFont); } static LRESULT CALLBACK PackSelWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) { switch(msg) { case WM_CREATE: { // we keep a pointer to the PS_CONTROLINFO structure, which points to the first package // which points to the next one // which points to the next one, etc. // each package points to its first file and its attributes // each file points to the next, etc. :) PS_CONTROLINFO *pCtrlInfo = new PS_CONTROLINFO; ZeroMemory(pCtrlInfo,sizeof(PS_CONTROLINFO)); HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); LOGFONT logFont; // font information GetObject(hFont,sizeof(logFont),&logFont); // get font information logFont.lfQuality = PROOF_QUALITY; logFont.lfWeight = FW_NORMAL; logFont.lfHeight -= 1; pCtrlInfo->hTitleFont = CreateFontIndirect(&logFont); // recreate the font, underlined GetObject(hFont,sizeof(logFont),&logFont); // get font information logFont.lfQuality = PROOF_QUALITY; logFont.lfWeight = FW_NORMAL; logFont.lfHeight += 2; pCtrlInfo->hPackageInfoFont = CreateFontIndirect(&logFont); // recreate the font, underlined pCtrlInfo->hTitleLine = CreatePen(PS_SOLID,1,RGB(210,210,210)); SIZE fontSize; HDC hDC = GetDC(hWnd); GetTextExtentPoint32(hDC,PS_OPTIONALTEXT,lstrlen(PS_OPTIONALTEXT),&fontSize); // hmm I think this will work ok :) pCtrlInfo->wRowHeight = (fontSize.cy << 1) + 1 + PS_PACKAGESPACE; // 1 (pen) + 4 (space) pCtrlInfo->wFileHeight = fontSize.cy + 1; // not too high ReleaseDC(hWnd,hDC); pCtrlInfo->hTitleUL = CreatePen(PS_SOLID,1,GetSysColor(COLOR_WINDOWTEXT)); pCtrlInfo->hCBPen = CreatePen(PS_SOLID,1,RGB(111,178,235)); pCtrlInfo->hBKBrush = CreateSolidBrush(RGB(224,237,252)); // pCtrlInfo->hDBKBrush = CreateSolidBrush(RGB(200,200,200)); OSVERSIONINFO osVers; osVers.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx(&osVers); if ((osVers.dwMajorVersion == 5) || ((osVers.dwMajorVersion == 4) && ((osVers.dwMinorVersion == 10) || (osVers.dwMinorVersion == 90)))) { pCtrlInfo->hHandCursor = LoadCursor(NULL,IDC_HAND); } else { pCtrlInfo->hHandCursor = LoadCursor(g_hInstance,MAKEINTRESOURCE(IDC_MYHAND)); } SetWindowLong(hWnd,0,(LONG)pCtrlInfo); // init scrollbar SendMessage(hWnd,PSM_SCROLLBARUPDATE,0,0); // SendMessage(hWnd,WM_ENABLE,IsWindowEnabled(hWnd),0); // change state of scrollbar (if necessary) } break; case WM_GETFONT: // not very important, but should be implemented { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); return(((pCtrlInfo) ? ((LPARAM)pCtrlInfo->hTitleFont) : (NULL))); // return title font } break; case PSM_SCROLLBARUPDATE: // if wParam is 1, we were called by the PSM_SCROLLBAR msg { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); RECT rcWnd; GetClientRect(hWnd,&rcWnd); // scrollbar info SCROLLINFO scrollInfo; scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; scrollInfo.nMin = 0; scrollInfo.nPos = pCtrlInfo->wScrollPos; scrollInfo.nPage = rcWnd.bottom; // height of client area // scrollInfo.nMax is tricky to compute // let's do it... :) scrollInfo.nMax = pCtrlInfo->wPackagesNum * pCtrlInfo->wRowHeight; PS_PACKAGELIST *pCurPackage = pCtrlInfo->pPackageList; while (pCurPackage != NULL) { if (pCurPackage->dwFlags & 1) // expanded { scrollInfo.nMax += pCurPackage->wTotalFiles * pCtrlInfo->wFileHeight; } pCurPackage = pCurPackage->next; } // I hate scrollbars if ((unsigned int)scrollInfo.nMax > scrollInfo.nPage) // scrollbar is shown { int iDelta = scrollInfo.nMax - scrollInfo.nPage; if (pCtrlInfo->wScrollPos > iDelta) // too much! (i.e. we're drawing over the edge) { pCtrlInfo->wScrollPos = iDelta; // go back scrollInfo.nPos = pCtrlInfo->wScrollPos; if (!wParam) // no call from routine, let's redraw ourselves { RECT rcWnd; GetClientRect(hWnd,&rcWnd); InflateRect(&rcWnd,-1,-1); // do not redraw border! InvalidateRect(hWnd,&rcWnd,false); UpdateWindow(hWnd); } } } else // no scrolling at all { // make sure we're at 0 pCtrlInfo->wScrollPos = 0; scrollInfo.nPos = 0; if (!wParam) // no call from routine, let's redraw ourselves { RECT rcWnd; GetClientRect(hWnd,&rcWnd); InflateRect(&rcWnd,-1,-1); // do not redraw border! InvalidateRect(hWnd,&rcWnd,false); UpdateWindow(hWnd); } } SetScrollInfo(hWnd,SB_VERT,&scrollInfo,true); return(0); } break; case PSM_SCROLLBAR: // wParam is new pos { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); WORD wOldPos = pCtrlInfo->wScrollPos; pCtrlInfo->wScrollPos = wParam; // new position SendMessage(hWnd,PSM_SCROLLBARUPDATE,1,0); RECT rcClient, rcWnd; // rcWnd will be used later to include the border in the invalidation region GetClientRect(hWnd,&rcClient); // this will be the clipping rect CopyMemory(&rcWnd,&rcClient,sizeof(RECT)); // make a copy InflateRect(&rcClient,-1,-1); // exclude border (otherwise it'll be scrolled to and smear around) ScrollWindowEx(hWnd,0,wOldPos - pCtrlInfo->wScrollPos,&rcClient,NULL,NULL,NULL,SW_INVALIDATE); // before we call an update, we need to include the border in the invalidation region HRGN hWndRgn = CreateRectRgnIndirect(&rcWnd); // entire window HRGN hClientRgn = CreateRectRgnIndirect(&rcClient); // client area HRGN hBorderRgn = CreateRectRgn(1,1,2,2); // the border CombineRgn(hBorderRgn,hWndRgn,hClientRgn,RGN_XOR); // XOR (we will be left with the border) InvalidateRgn(hWnd,hBorderRgn,false); UpdateWindow(hWnd); // clean up DeleteObject(hBorderRgn); DeleteObject(hClientRgn); DeleteObject(hWndRgn); return(0); } break; case WM_MOUSEWHEEL: { UINT uiScrollLines; PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); int iScrollPos = (int)(pCtrlInfo->wScrollPos); // signed (boundaries check come later) SCROLLINFO scrollInfo; scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_RANGE | SIF_PAGE; // get nMax GetScrollInfo(hWnd,SB_VERT,&scrollInfo); scrollInfo.nMax -= scrollInfo.nPage; // no problems here (we have a scroll bar, meaning the max is bigger than the page) - this is how much we can actually scroll if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES,0,&uiScrollLines,0) == 0) // got nothing { uiScrollLines = 3; } // scrolling is actually: iScrollPos -= ((short)HIWORD(wParam) * pCtrlInfo->wFileHeight * (int)uiScrollLines) / WHEEL_DELTA; // boundaries check if (iScrollPos < 0) { iScrollPos = 0; } else if (iScrollPos > scrollInfo.nMax) { iScrollPos = scrollInfo.nMax; } if ((int)(pCtrlInfo->wScrollPos) != iScrollPos) // change { SendMessage(hWnd,PSM_SCROLLBAR,iScrollPos,0); } return(0); } break; case WM_VSCROLL: {/* // TOOD is this needed? - nope, coz we're not supporting disable/enable mode if (!IsWindowEnabled(hWnd)) // disabled { return(0); }*/ PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); int iScrollPos = (int)(pCtrlInfo->wScrollPos); // signed (boundaries check come later) RECT rcWnd; GetClientRect(hWnd,&rcWnd); SCROLLINFO scrollInfo; scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_RANGE | SIF_PAGE; // get nMax GetScrollInfo(hWnd,SB_VERT,&scrollInfo); scrollInfo.nMax -= scrollInfo.nPage; // no problems here (we have a scroll bar, meaning the max is bigger than the page) - this is how much we can actually scroll // scrolling is a mess // SB_BOTTOM/SB_TOP/SB_LINEDOWN/SB_LINEUP are rather easy // SB_PAGEDOWN and SB_PAGEUP were a mess, but it works rather well now :) switch(LOWORD(wParam)) { case SB_BOTTOM: iScrollPos = scrollInfo.nMax; break; case SB_LINEDOWN: iScrollPos += pCtrlInfo->wFileHeight; break; case SB_LINEUP: iScrollPos -= pCtrlInfo->wFileHeight; break; // boundary check here case SB_PAGEDOWN: if (iScrollPos+rcWnd.bottom <= scrollInfo.nMax) iScrollPos += (WORD)rcWnd.bottom; else iScrollPos = scrollInfo.nMax; break; case SB_PAGEUP: if (iScrollPos >= (WORD)rcWnd.bottom) iScrollPos -= (WORD)rcWnd.bottom; else iScrollPos = 0; break; case SB_THUMBTRACK: iScrollPos = HIWORD(wParam); break; case SB_TOP: iScrollPos = scrollInfo.nMin; break; } if (iScrollPos < 0) { iScrollPos = 0; } else if (iScrollPos > scrollInfo.nMax) { iScrollPos = scrollInfo.nMax; } if ((int)(pCtrlInfo->wScrollPos) != iScrollPos) // change { SendMessage(hWnd,PSM_SCROLLBAR,iScrollPos,0); } return(0); } break; case WM_GETDLGCODE: // need to get direction keys msgs { return(DLGC_WANTARROWS); } break; case WM_KEYDOWN: // handle some more user input { if (wParam == VK_DOWN) { SendMessage(hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEDOWN,0),NULL); // use WM_VSCROLL } else if (wParam == VK_UP) { SendMessage(hWnd,WM_VSCROLL,MAKEWPARAM(SB_LINEUP,0),NULL); // use WM_VSCROLL } return(0); } break; case WM_SETCURSOR: { RECT rcTemp; UINT mouseMsg = HIWORD(lParam); if (/*(!IsWindowEnabled(hWnd)) || */((mouseMsg != WM_MOUSEMOVE) && (mouseMsg != WM_LBUTTONDOWN))) // only these msgs are involved { return(false); } // check "collision" for each package PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); RECT rcWnd; GetClientRect(hWnd,&rcWnd); PS_PACKAGELIST *pCurPackage = pCtrlInfo->pPackageList; POINT ptMouse; GetCursorPos(&ptMouse); ScreenToClient(hWnd,&ptMouse); bool fSetCursor = false; int iPosY = PS_PACKAGEPOS_Y - pCtrlInfo->wScrollPos; while (pCurPackage != NULL) { rcTemp.left = PS_PACKAGEPOS_X; rcTemp.right = rcTemp.left + pCurPackage->TitleSize.cx; rcTemp.top = iPosY; rcTemp.bottom = rcTemp.top + pCurPackage->TitleSize.cy; bool fLastState = ((pCurPackage->dwFlags & 2) >> 1) & 1; if (PtInRect(&rcTemp,ptMouse)) { pCurPackage->dwFlags |= 2; // hot-track SetCursor(pCtrlInfo->hHandCursor); fSetCursor = true; if (mouseMsg == WM_LBUTTONDOWN) { bool fOldState = !(pCurPackage->dwFlags & 1); // reverse pCurPackage->dwFlags &= ~1; // clear pCurPackage->dwFlags |= fOldState & 1; // invalidate entire item (background) rcTemp.left = 1; rcTemp.top = iPosY + (pCurPackage->TitleSize.cy >> 1) - ((pCtrlInfo->wRowHeight-(PS_PACKAGESPACE>>1)) >> 1); rcTemp.right = rcWnd.right - 1; rcTemp.bottom = rcWnd.bottom - 1; // we redraw to the bottom of the control (necessary) - no need for last pixel, it's the frame SendMessage(hWnd,PSM_SCROLLBARUPDATE,0,0); InvalidateRect(hWnd,&rcTemp,false); UpdateWindow(hWnd); } } else { pCurPackage->dwFlags &= ~2; // bye (no hot-track) } // only redraw if we must if ((unsigned long)fLastState != (((pCurPackage->dwFlags & 2) >> 1) & 1)) { rcTemp.bottom++; // will force a refresh InvalidateRect(hWnd,&rcTemp,false); UpdateWindow(hWnd); } if (fSetCursor) { iPosY += pCtrlInfo->wRowHeight; pCurPackage = pCurPackage->next; continue; // we don't break, read below } // now check for icon rcTemp.top = iPosY + 1; rcTemp.bottom = rcTemp.top + pCurPackage->TitleSize.cy - 1; rcTemp.left = (PS_PACKAGEPOS_X - (rcTemp.bottom-rcTemp.top)) >> 1; rcTemp.right = (rcTemp.bottom - rcTemp.top) + rcTemp.left; // latest change: package is only changeable if it is optional (Kai) if ((pCurPackage->packageInfo.fOptional) && (PtInRect(&rcTemp,ptMouse))) { SetCursor(pCtrlInfo->hHandCursor); fSetCursor = true; if (mouseMsg == WM_LBUTTONDOWN) { pCurPackage->packageInfo.fSelected = !pCurPackage->packageInfo.fSelected; SendMessage(GetParent(hWnd),PSN_PACKAGESELCHANGE,0,0); // notify parent // invalidate entire item (background) PS_GetPackageRect(hWnd,pCtrlInfo,iPosY,pCurPackage,&rcTemp); SendMessage(hWnd,PSM_SCROLLBARUPDATE,0,0); InvalidateRect(hWnd,&rcTemp,false); UpdateWindow(hWnd); } } // we could stop here if the cursor was set for one item // coz it's enough // but we might need to unset the hot-tracking for other items, sux iPosY += pCtrlInfo->wRowHeight + ((pCurPackage->dwFlags & 1) ? (pCtrlInfo->wFileHeight*pCurPackage->wTotalFiles) : (0)); pCurPackage = pCurPackage->next; } if (!fSetCursor) // set arrow { SetCursor(LoadCursor(NULL,IDC_ARROW)); } if ((GetFocus() != hWnd) && (((mouseMsg == WM_LBUTTONDOWN) || (mouseMsg == WM_RBUTTONDOWN)))) // keyboard focus needs to be set { SetFocus(hWnd); } return(false); } break; // painting case WM_ERASEBKGND: // this should'nt be sent. ever. but just in case. { return(1); } break; case WM_PRINTCLIENT: // not REALLY needed, but it's rather easy, and might be useful later { RECT rcWnd; GetWindowRect(hWnd,&rcWnd); PS_DrawControl(hWnd,(HDC)wParam,&rcWnd); } break; case WM_PAINT: { PAINTSTRUCT ps; PS_DrawControl(hWnd,BeginPaint(hWnd,&ps),&(ps.rcPaint)); EndPaint(hWnd,&ps); return(0); } break; case PSM_ADDPACKAGE: // add a new package. will be inserted at positon given by wParam, lParam is a pointer to a PS_PACKAGEINFO structure that contains info about the new package. returns unique ID of package { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); PS_PACKAGELIST *node = new PS_PACKAGELIST; // allocate // not a big fan of these stuff. but might as well get used to it :) ZeroMemory(node,sizeof(PS_PACKAGELIST)); CopyMemory(&(node->packageInfo),(PS_PACKAGEINFO*)lParam,sizeof(PS_PACKAGEINFO)); // find insertion position WORD wPos = 0; // we're not always entering the loop PS_PACKAGELIST *pCurPos = pCtrlInfo->pPackageList; PS_PACKAGELIST *pLastPos = NULL; while (pCurPos != NULL) { if (wPos++ == wParam) { wPos--; // right pos break; } pLastPos = pCurPos; pCurPos = pCurPos->next; } // insertion point is pLastFile if (pLastPos == NULL) // new head { node->next = pCtrlInfo->pPackageList; pCtrlInfo->pPackageList = node; // new head } else // somewhere in the middle/end { node->next = pLastPos->next; // if another item is after pCurPos, we stick it to the end of node pLastPos->next = node; // new item after pCurPos is node } HDC hDC = GetDC(hWnd); HFONT hOldFont = (HFONT)SelectObject(hDC,pCtrlInfo->hTitleFont); // select our favourite font // we now do lpDisplayText and wTitleWidth char *lpTemp = new char[lstrlen(node->packageInfo.lpTitle) + 1]; lstrcpy(lpTemp,node->packageInfo.lpTitle); SIZE titleSize; GetTextExtentPoint32(hDC,lpTemp,lstrlen(lpTemp),&titleSize); if (lstrlen(lpTemp) > 3) // only if more than 3! { RECT rcWnd; GetClientRect(hWnd,&rcWnd); char *lpDotIndex = lpTemp + lstrlen(lpTemp) - 3; // 3 is length of dots. fixed, for now while (titleSize.cx+PS_PACKAGEPOS_X >= (rcWnd.right*0.5)) // no more than 50% of client width { lstrcpy(lpDotIndex,"..."); lpDotIndex--; GetTextExtentPoint32(hDC,lpTemp,lstrlen(lpTemp),&titleSize); } } // now copy it back node->lpDisplayText = new char[lstrlen(lpTemp) + 1]; lstrcpy(node->lpDisplayText,lpTemp); CopyMemory(&(node->TitleSize),&titleSize,sizeof(SIZE)); // node->wTitleWidth = (WORD)titleSize.cx; delete [] lpTemp; SelectObject(hDC,hOldFont); ReleaseDC(hWnd,hDC); pCtrlInfo->wPackagesNum++; SendMessage(hWnd,PSM_SCROLLBARUPDATE,0,0); return(wPos); } break; case PSM_ADDFILE: // adds a new file (files are added alphabetically). the package it should be put in is identified by wParam, lParam is a pointer to a PS_FILEINFO structure. return value is unique ID of file. { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); WORD wPos = 0; PS_PACKAGELIST *pCurPos = pCtrlInfo->pPackageList; while (pCurPos != NULL) { if (wPos++ == wParam) { // wPos--; // we don't use this again later break; } pCurPos = pCurPos->next; } if (pCurPos != NULL) // yay, we got the package { PS_FILELIST *node = new PS_FILELIST; ZeroMemory(node,sizeof(PS_FILELIST)); CopyMemory(&(node->fileInfo),(PS_FILEINFO*)lParam,sizeof(PS_FILEINFO)); CharLower(node->fileInfo.lpFilename); // convert to lower case // now add the display text! char szTemp[128], szFileSize[32];; // will hold size SIZE extraSize, textSize; // extra text (szTemp) size RECT rcWnd; GetClientRect(hWnd,&rcWnd); Format_DisplaySize(szFileSize,node->fileInfo.dwFileSize); wsprintf(szTemp," (%s)",szFileSize); HDC hDC = GetDC(hWnd); HFONT hOldFont = (HFONT)SelectObject(hDC,pCtrlInfo->hTitleFont); char *lpTempText = new char[lstrlen(node->fileInfo.lpFilename) + 1]; // used to shorten the name. temporary. lstrcpy(lpTempText,node->fileInfo.lpFilename); if (lstrlen(lpTempText) > 3) { GetTextExtentPoint32(hDC,szTemp,lstrlen(szTemp),&extraSize); GetTextExtentPoint32(hDC,lpTempText,lstrlen(lpTempText),&textSize); char *lpDotIndex = lpTempText + lstrlen(lpTempText) - 3; // 3 is length of dots. fixed, for now while (PS_FILEPOS_X+textSize.cx+extraSize.cx > (rcWnd.right >> 1)) // too big { lstrcpy(lpDotIndex,"..."); lpDotIndex--; GetTextExtentPoint32(hDC,lpTempText,lstrlen(lpTempText),&textSize); } } node->lpDisplayText = new char[lstrlen(lpTempText) + lstrlen(szTemp) + 1]; // final copy lstrcpy(node->lpDisplayText,lpTempText); lstrcat(node->lpDisplayText,szTemp); delete [] lpTempText; SelectObject(hDC,hOldFont); ReleaseDC(hWnd,hDC); WORD wFilePos = 0; PS_FILELIST *pCurFile = pCurPos->pFileList; PS_FILELIST *pLastFile = NULL; while (pCurFile != NULL) { // lstrcmpi isn't needed, it's always lower case (we make sure) if (lstrcmp(pCurFile->fileInfo.lpFilename,node->fileInfo.lpFilename) >= 0) // insert here { if (wFilePos > 0) wFilePos--; break; } pLastFile = pCurFile; pCurFile = pCurFile->next; wFilePos++; } // insertion point is pLastFile if (pLastFile == NULL) // new head { node->next = pCurPos->pFileList; pCurPos->pFileList = node; // new head } else // somewhere in the middle/end { node->next = pLastFile->next; // if another item is after pCurFile, we stick it to the end of node pLastFile->next = node; // new item after pCurFile is node } // another file added pCurPos->wTotalFiles++; // per-package pCurPos->dwTotalSize += node->fileInfo.dwFileSize; SendMessage(hWnd,PSM_SCROLLBARUPDATE,0,0); return(wFilePos); } return(0); } break; case PSM_SELECTPACKAGE: // wParam identifies the package, if lParam is TRUE, the package will be selected, if lParam is FALSE, the package will be un-selected :) { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); WORD wPos = 0; int iPosY = PS_PACKAGEPOS_Y - pCtrlInfo->wScrollPos; PS_PACKAGELIST *pCurPos = pCtrlInfo->pPackageList; while (pCurPos != NULL) { // we need to find our position PS_FILELIST *pCurFile = pCurPos->pFileList; if (wPos++ == wParam) { if (pCurPos->packageInfo.fSelected != (lParam & 1)) // only if different { pCurPos->packageInfo.fSelected = lParam & 1; // compiler warnings suck RECT rcPackage; PS_GetPackageRect(hWnd,pCtrlInfo,iPosY,pCurPos,&rcPackage); InvalidateRect(hWnd,&rcPackage,false); UpdateWindow(hWnd); } return(1); } iPosY += pCtrlInfo->wRowHeight + ((pCurPos->dwFlags & 1) ? (pCtrlInfo->wFileHeight*pCurPos->wTotalFiles) : (0)); pCurPos = pCurPos->next; } return(0); } break; case PSM_GETPACKAGECOUNT: // ... :) { return(((PS_CONTROLINFO*)GetWindowLong(hWnd,0))->wPackagesNum); } break; case PSM_GETPACKAGEINFO: // wParam is ID of package, return value is a pointer to the package info (PS_PACKAGEINFO) - look but don't touch. { WORD wPos = 0; PS_PACKAGELIST *pCurPos = ((PS_CONTROLINFO*)GetWindowLong(hWnd,0))->pPackageList; while (pCurPos != NULL) { if (wPos++ == wParam) { break; } pCurPos = pCurPos->next; } if (pCurPos != NULL) // yay, we got the package { return((LRESULT)&(pCurPos->packageInfo)); } return(0); } break; case PSM_GETFILECOUNT: // wParam is ID of package { WORD wPos = 0; PS_PACKAGELIST *pCurPos = ((PS_CONTROLINFO*)GetWindowLong(hWnd,0))->pPackageList; while (pCurPos != NULL) { if (wPos++ == wParam) { return(pCurPos->wTotalFiles); } pCurPos = pCurPos->next; } return(0); } break; case PSM_GETFILEINFO: // wParam is ID of file in package (wait a sec), lParam is a pointer to a PS_PACKAGEINFO structure returned by PSM_GETPACKAGEINFO (now u got it :)). return value is a pointer to a PS_FILEINFO structure { // :) // so easy this is almost devilish // well, it could be easier, actually :) // first we need to find the package PS_PACKAGEINFO *pPackageInfo = (PS_PACKAGEINFO*)lParam; PS_PACKAGELIST *pCurPos = ((PS_CONTROLINFO*)GetWindowLong(hWnd,0))->pPackageList; while (pCurPos != NULL) { if (&(pCurPos->packageInfo) == pPackageInfo) // compare address. comparison can also be made according to pCurPos, since package info is right at the beginning { WORD wPos = 0; PS_FILELIST *pCurFile = pCurPos->pFileList; while (pCurFile != NULL) { if (wPos++ == wParam) { return((LPARAM)&(pCurFile->fileInfo)); } pCurFile = pCurFile->next; } } pCurPos = pCurPos->next; } return(NULL); } break;/* case WM_ENABLE: // mode is changing... { InvalidateRect(hWnd,NULL,false); UpdateWindow(hWnd); return(0); } break;*/ case WM_DESTROY: { PS_CONTROLINFO *pCtrlInfo = (PS_CONTROLINFO*)GetWindowLong(hWnd,0); PS_PACKAGELIST *pCurPackage = pCtrlInfo->pPackageList; while (pCurPackage != NULL) { PS_PACKAGELIST *pNextPackage = pCurPackage->next; delete [] pCurPackage->packageInfo.lpTitle; // delete it delete [] pCurPackage->lpDisplayText; // now delete all files PS_FILELIST *pCurFile = pCurPackage->pFileList; while (pCurFile != NULL) { PS_FILELIST *pNextFile = pCurFile->next; delete [] pCurFile->fileInfo.lpFilename; delete [] pCurFile->lpDisplayText; delete pCurFile; pCurFile = pNextFile; } delete pCurPackage; pCurPackage = pNextPackage; } DeleteObject(pCtrlInfo->hTitleFont); DeleteObject(pCtrlInfo->hTitleLine); DeleteObject(pCtrlInfo->hPackageInfoFont); DeleteObject(pCtrlInfo->hTitleUL); DeleteObject(pCtrlInfo->hCBPen); DeleteObject(pCtrlInfo->hBKBrush); // DeleteObject(pCtrlInfo->hDBKBrush); delete pCtrlInfo; return(0); } break; } return(DefWindowProc(hWnd,msg,wParam,lParam)); } void PackageSelector_Init(void) { WNDCLASS wcl; wcl.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC; wcl.lpfnWndProc = PackSelWndProc; wcl.cbClsExtra = 0; wcl.cbWndExtra = sizeof(PS_CONTROLINFO*); wcl.hInstance = g_hInstance; wcl.hCursor = NULL;//LoadCursor(NULL,IDC_ARROW);; wcl.lpszClassName = PACKAGESELECTOR_CLASSNAME; // wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcl.hbrBackground = NULL; wcl.hIcon = NULL; wcl.lpszMenuName = NULL; RegisterClass(&wcl); }